#pragma once
#include<iostream>
#include<fstream>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<pthread.h>
#include<vector>

enum 
{
    SocketError=2,BindError,ListenError
};

const int backlog=10;
const std::string sep="\r\n";
std::string whb_root="./whb_root";//whb根目录

class Resquest
{
public:
    std::string _method;
    std::string _url;
    std::string _version;

    std::vector<std::string> _heads;//头部字段
    
    std::string _text;//正文

    std::string _path;//资源路径: whb根目录+url

    void deserialize(std::string buf)
    {
        //buf中空行前都是头部字段，空行后就是正文
        int init_pos=0;
        while(1)
        {
            size_t goal_pos=buf.find(sep,init_pos);
            if(goal_pos==std::string::npos)break;
            std::string line=buf.substr(init_pos,goal_pos-init_pos);
            if(init_pos==0){
                SplitResquestLine(line);
                init_pos=goal_pos+sep.size();
                continue;
            }
            init_pos=goal_pos+sep.size();
            if(line=="")break;
            _heads.push_back(line);
        }
        _text=buf.substr(init_pos);
    }
    void SplitResquestLine(std::string reqline)
    {
        //请求方法 url 协议版本  咱们从中提取出url就行
        size_t first_space_pos=reqline.find(" ");
        size_t second_space_pos=reqline.find(" ",first_space_pos+1);

        _method=reqline.substr(0,first_space_pos);
        _url=reqline.substr(first_space_pos+1,second_space_pos-first_space_pos-1);
        _version=reqline.substr(second_space_pos+1);

        //根据utl组建path
        if(_url=="/" || _url=="/index"){
            _path=whb_root+"/index";
            return;
        }
        // /a/b/c
        _path=whb_root+_url;
    }

    void DebugPrint()
    {
        std::cout<<"请求方法:"<<_method<<std::endl;
        std::cout<<"请求url:"<<_url<<std::endl;
        std::cout<<"协议版本:"<<_version<<std::endl;

        std::cout<<"相对whb根目录下的绝对资源路径:"<<_path<<std::endl;

        std::cout<<"请求头部字段:"<<std::endl;
        for(const auto& str:_heads)
            std::cout<<str<<std::endl;

        std::cout<<"\n请求正文:"<<_text<<std::endl;
    }
};

class server
{
private:
    int _sockfd;

public:
    server(int port)
    {
        //创建网络套接字
        _sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd<0)
        {
            std::cout<<"create socket error,errno:"<<errno<<",strerror:"<<strerror<<std::endl;
            exit(SocketError);
        }
        //地址复用
        int opt=1;
        setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

        //绑定
        struct sockaddr_in local;//函数栈帧内的一个局部sock类型的对象
        memset(&local,0,sizeof(local));

        local.sin_family=AF_INET;
        local.sin_port=htons(port);
        local.sin_addr.s_addr=INADDR_ANY;

        if(bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)//将数据填充到内核中的tcp_sock对象中
        {
            std::cout<<"bind socket error,errno:"<<errno<<",strerror:"<<strerror<<std::endl;
            exit(BindError);
        }
        //监听
        if(listen(_sockfd,backlog)<0)//listen的主要作用：将服务端套接字变成被动监听状态，使其能够收到来自其它套接字的连接请求
        {
            std::cout<<"listen socket error,errno:"<<errno<<",strerror:"<<strerror<<std::endl;
            exit(ListenError);
        }
    }
    int Accept(std::string& client_ip,uint16_t& client_port)
    {
        struct sockaddr_in client;//存放来自对方的套接字信息
        socklen_t len=sizeof(client);
        int rcv_fd=accept(_sockfd,(struct sockaddr*)&client,&len);//三次握手成功后，OS会把新建立的链接放到“全链接队列”，accept会将“全链接队列”中的链接拿到上层，把报文中发送方的套接字信息存储下来，并返回该报文所在的接受缓冲区
        if(rcv_fd<0)
        {
            std::cout<<"accept socket error,errno:"<<errno<<",strerror:"<<strerror<<std::endl;
            exit(ListenError);
        }
        char buf[64];
        inet_ntop(AF_INET,&client.sin_addr,buf,sizeof(buf));//将对方的ip信息由"网络字节序转"为"主机字节序"，并将转换后的结果存放到buf中
        client_ip=buf;
        client_port=ntohs(client.sin_port);
        
        return rcv_fd;
    }

    void Start()
    {
        while(1)
        {
            std::string client_ip;
            uint16_t client_port;
            int recv_fd=Accept(client_ip,client_port);//主线程阻塞式接收

            //将接收到的套接字交给子线程处理
            pthread_t tid;
            pthread_create(&tid,nullptr,Entry,&recv_fd);
        }
    }

    static std::string MakeEchoText(const std::string& path)
    {
        std::ifstream in(path.c_str(),std::ios_base::binary | std::ios_base::in);
        if(in.is_open()==false)return "404  NOT FIND";
        std::string res,line;
        while(std::getline(in,line))res+=line;
        return res;
    }

    static void* Entry(void* arg)
    {
        pthread_detach(pthread_self());//线程分离

        int recv_fd=*(static_cast<int*>(arg));

        //事务处理
        char buf[10240];
        int ret=recv(recv_fd,buf,sizeof(buf),0);
        if(ret>0)
        {
            //将buf宅分成 头部字段 + 正文 两部分
            // std::cout<<buf<<std::endl;
            Resquest req;
            req.deserialize(buf);
            // req.DebugPrint();

            //构建应答报文
            std::string echo_line="HTTP/1.1 200 success\r\n";//响应行：协议版本、响应状态码、状态码描述字段
            std::string echo_text=MakeEchoText(req._path);
            // std::cout<<MakeEchoText(req._path)<<std::endl;
            // std::string echo_text="this is a news for test...";
            std::string echo_heads=std::string("Content-Length: ")+std::to_string(echo_text.size())+"\r\n";
            std::string blank_line="\r\n";

            std::string echo_res=echo_line+echo_heads+blank_line+echo_text;

            // std::cout<<echo_text<<std::endl;

            send(recv_fd,echo_res.c_str(),echo_res.size(),0);

            // std::cout<<echo_text<<std::endl;
        }

        return nullptr;
    }
};